                        Machine Lanuage - Part V

                          by Lyle Giese (LYLEG)


This month, I decided to keep my promise to write a program in a label
based assembler. The program is a SEQ file reader with the output going
to the screen or the printer. The printer device can be selected (4-7)
as well as the secondary address (limited to 0-9).

The reason for using ML here should be obvious to anyone who has written
a file reader in BASIC. You go to sleep after about the second screen
full of information. Speed loaders of course help, but nothing can beat
ML here.

I wouldn't expect ANYONE to enter this program without a label based
assembler. This program would be just too complicated to do it with a
simple assembler. I don't think I could have written it with a simple
assembler either.

The assembler I used was PAL. So if you have a copy of the SYSMASS from
Transactor it will be compatible with that. And with only minor changes,
it would also be compatible with MADS. I have not tried to convert it
yet, but these two lines will help:

OPEN8,8,8,"FILENAME,S,W":POKE22,35:CMD8:LIST
PRINT#8:CLOSE8

This will create a SEQ file without the line numbers that MADS can use.
PAL relies on the BASIC editor and stores its files as PRG files.

One of the biggest advantages of label based assemblers is the work they
take away from you. First you can save your equates and load them as a
unit and only type them in once. That way you can type in the equates
for the entire KERNAL Jump Table and with PAL save them with the .SST
command. Then the next time you want anything from the KERNAL, you would
just .LST to load that table in and you have the entire KERNAL Jump
Table.

But since this is our first program we have to type in the KERNAL
routines we will need to use. And I did that right at the start of the
source code. Then I put in the rest of the memory locations I needed and
added to it as I needed them in the program.

Also the label based assembler will help to abstract the process. You
don't have to keep thinking about all of those numbers and keeping them
straight, like the KERNAL routine to read the Status byte after an I/O
operation. You don't have to remember each time you call it that it is
at $FFB7, you just call it by the name Commodore gave it, READST.

The same goes for branch instructions or for places in the program. Go
to line 510. It is the start of the program, and if I want to jump to
the start of the program, I just put in a line 'JMP START'. That is a
lot easier than remembering 'JMP $C000'.

So let's take a look at the program and see how the little thing works
(quite well actually!).

We start out at line 50 by issuing a call to the PAL assembler to start
the assembly. Before assembling, you have to load and run PAL which then
seals itself off in high BASIC memory and is vectored from the USR
function at 700. And then you enter or load your source code and issue
the BASIC command run, and PAL takes over.

The next line (75) contains instructions to the assembler. It is telling
the assembler what to do with the object code and the printed output.
You can send the object directly to memory, disk, cassette, or nowhere.
Why would you not want the object code sent some where? If you just
wanted to check for syntax errors in the source code, you would run PAL
with no object or printed output. PAL can run through this very fast and
show just your syntax errors. (No, it won't show your logical errors.)

Another thing you get besides the object code is a printed output. This
makes for a nice neat record of your code. It is useful when you have to
debug your programs. If you want it generated this output can be sent to
disk or printer.

In the equates, I used the Commodore names for the KERNAL routines used.
The rest of the equates are varibles that I needed as I wrote the
program.

Then on to the program itself. The first thing I do is change the screen
and border color to black and then the character color to yellow. These
are my preferences and can of course be changed to whatever you want.

Then we need to print on the screen the opening message. Again, the
label based assembler is very handy at this point. I knew I wanted a
message printed here telling you how to use the program and prompting
you for the filename. But I just called this 'AMESS' and continued on,
knowing that later I would need to write that message and would just
call it 'AMESS'.

Since I also realized that I would need to send several messages to the
screen, I would save time and trouble and make the screen messaging a
subroutine. That way it would need to be written only once. I called it
'MESSOUT'.

Again, to emphasize the point, the label based assembler is handy. I
don't need or care what address the subroutine is at. The assembler will
take care of that when you assemble the program. It will also make any
changes in the program; all calls to that subroutine will be adjusted
accordingly.

Now just how do we pass the address of our message to the messout
routine? That is easy (at least for me). At the address INDEX and
INDEX+1 we store the start address of the message. And we put a zero
byte at the end of the message to mark the end. But what do the symbols
< and > mean here?

The assembler can do math for you to determine the numbers you want. The
< and > symbols get the Low order byte and the HIgh order byte of the
address or number AMESS means. In this example, AMESS will end up at
$C22B in the finished program. The high order byte is $C2 and the low
order byte is $2B.

Just for kicks how do I convert a decimal address into this format? As
in my first column the formula is:

Hibyte = int(address/256)
Lobyte = address-(int(address/256)*256)

Isn't it nice that the assembler will do this math for us? (In case you
havn't guessed, I am pushing the label based assemblers for any serious
work in ML.)

If we look at the message starting at line 5000 (see how we put labels
at certain points in the program?), we start by putting a zero in the Y
reg and use that as a counter.

In the next instruction, load the first byte of the message into the
accumlator using indrect Y addressing. That is the address at INDEX and
INDEX+1 will be added to the Y reg and that is where we will get our
byte from.

In our example it will be the first byte of the AMESS. That first byte
is the character CLEAR/HOME. The next instruction is checking for a zero
byte that would signal the end of the message. Then we output the
character to the default device which is the screen. Then we increment
our counter, the Y reg.

Next we have to check to see if the Y reg rolled over from $FF(255) to
$00. Why? Because if we did not then we would pick up the first
character of our message and not the 257th character. So if we have
already outputed 256 characters, we have to increment the high byte of
our index at the address index+1 and then continue picking up and
outputing the rest of the message.

When we get to the end (our zero byte) we will branch to the RTS
instruction and return to the main part of the program.

The syntax used in making the text strings for the assembler to use is
where most assemblers do differ. In my examples PAL uses the puesdo-opt
(that is commands for the assembler) .asc and double quotes " to insert
the strings. In MADS the puesdo-opt is .byte and a single quote '
followed by the text string. At this point you will have to refer to
your manual for the assembler you are using.

It looks like I have covered enough ground for one month. Next month we
will explore getting the filename and opening the disk files. Included
this month are the parts of the finished source covered here and the
full object code of the program. The object code needs to be loaded ,8,1
and then SYS49152 to use it.

Until next month!

Lyle Giese (LYLEG)
